﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tools.Plugins.IconConver
{
    public class IconService
    {

        public ImageEntry[] ReadIconToImages(byte[] iconData)
        {
            short imageCount = BitConverter.ToInt16(iconData, 4);
            ImageEntry[] images = new ImageEntry[imageCount];

            int offset = 6;
            for (int i = 0; i < imageCount; i++)
            {
                short imageWidth = iconData[offset];
                short imageHeight = iconData[offset + 1];
                int imageDataOffset = BitConverter.ToInt32(iconData, offset + 12);
                int imageDataSize = BitConverter.ToInt32(iconData, offset + 8);
                byte[] imageData = new byte[imageDataSize];
                Array.Copy(iconData, imageDataOffset, imageData, 0, imageDataSize);
                images[i] = new ImageEntry { Buffer = imageData, Height = imageHeight, Width = imageWidth };
                offset += 16;
            }

            return images;

        }
        private Image ResizeImage(Image image, int width, int height)
        {
            return new Bitmap(image, new Size(width, height));
        }
        public byte[] ConvertToIcon(Image[] images)
        {
            ArgumentNullException.ThrowIfNull(images);
            ImageEntry[] imageDataList = images.Select(n => ToIcon(n, 16, 16)).ToArray();
            return ToArray(imageDataList);
        }

        ImageEntry ToIcon(Image image, int width, int height)
        {
            using var msImg = new MemoryStream();
            var imgEntry = new ImageEntry { Width = width, Height = height };
            using Image resizedImage = ResizeImage(image, imgEntry.Width, imgEntry.Height);
            resizedImage.Save(msImg, ImageFormat.Png);
            imgEntry.Buffer = msImg.ToArray();
            return imgEntry;
        }
        byte[] ToArray(ImageEntry[] entries)
        {
            using var msIco = new MemoryStream();
            using var bin = new BinaryWriter(msIco);
            bin.Write((short)0);          
            bin.Write((short)1);            
            bin.Write((short)entries.Length);  

            int offset = 6 + entries.Length * 16;
            foreach (var imgEntry in entries)
            {
                bin.Write((byte)imgEntry.Width);  // 6 图标宽度
                bin.Write((byte)imgEntry.Height);  // 7 图标高度
                bin.Write((byte)0);         // 8 颜色数（若像素位深>=8，填0。这是显然的，达到8bpp的颜色数最少是256，byte不够表示）
                bin.Write((byte)0);         // 9 保留。必须为0
                bin.Write((short)0);        // 10-11 调色板
                bin.Write((short)32);       // 12-13 位深
                bin.Write((int)imgEntry.Length); // 14-17 位图数据大小
                bin.Write(offset);           // 18-21 位图数据起始字节
                offset += imgEntry.Length;
            }
            foreach (ImageEntry imageEntry in entries)
            {
                bin.Write(imageEntry.Buffer);
            }
            bin.Flush();
            bin.Seek(0, SeekOrigin.Begin);
            return msIco.ToArray();
        }

        public byte[] ConvertToIcon(Image image, int[] sizes = null)
        {
            if (image == null) throw new ArgumentException("Image is null");
            int[] tmpSize = sizes;
            if (tmpSize == null) tmpSize = new[] { 128, 64, 32 };
            ImageEntry[] imageDataList = tmpSize.Select(n => ToIcon(image, n, n)).ToArray();
            return ToArray(imageDataList);
        }
    }

    public class ImageEntry : IDisposable
    {
        public byte[] Buffer { get; set; }

        public int Width { get; set; }
        public int Height { get; set; }
        public int Length { get { return Buffer == null ? 0 : Buffer.Length; } }

        Image image;
        MemoryStream stream;
        private bool disposedValue;

        public Image GetImage()
        {
            if (image != null) return image;
            if (Buffer == null) return null;
            try
            {
                stream = new MemoryStream(Buffer);
                image = Image.FromStream(stream);
                return image;
            }
            catch (Exception)
            {

                throw;
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (image != null)
                    {
                        image.Dispose();
                        image = null;
                    }
                    if (stream != null)
                    {
                        stream.Dispose();
                        stream = null;
                    }
                }

                disposedValue = true;
            }
        }

        ~ImageEntry()
        {

            Dispose(disposing: false);
        }

        public void Dispose()
        {

            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }

}
